﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Luminal;
using System.IO;
using System.Reflection;

namespace Luminal_Test
{
    class Luminal_Test
    {
        const int dataLength = 4;
        static Context context;
        static CommandQueue commandQueue;
        static MemoryObject memoryObject;
        static float[] data = new float[dataLength];
        static float[] newData = new float[dataLength];
        static MemoryObject image2DObject;
        static MemoryObject image3DObject;
        static Sampler sampler;
        static Program program;
        static Kernel kernel;

        static void Header(string value)
        {
            Console.ForegroundColor = ConsoleColor.Red;
            Console.Write(value);
            Console.ForegroundColor = ConsoleColor.White;
        }

        static void TestHeader(string value)
        {
            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.WriteLine(value);
            Console.ForegroundColor = ConsoleColor.White;
        }

        static void BreakLine()
        {
            Console.WriteLine();
        }

        static void TryExecute(Action action)
        {
            //try
            //{
                action();
                Console.WriteLine("Success");
            //}
            //catch (Exception e)
            //{
            //    Console.WriteLine("Failed: {0}", e.Message);
            //    Environment.Exit(0);
            //}
        }

        static void Main(string[] args)
        {
            Console.Title = "Luminal Test";
            Console.BufferHeight = 500;
            Console.BufferWidth = 120;

            PlatformTest();

            DeviceTest();

            ContextTest();

            CommandQueueTest();

            BufferTest();

            Image2DTest();

            Image3DTest();

            Image2D3DCopyTest();

            Image3D2DCopyTest();

            SamplerTest();

            ProgramTest();

            KernelTest();

            DisposalTest();

            TestHeader("***All Tests Passed***");
        }

        private static void PlatformTest()
        {
            TestHeader("***Platform Test***");

            Header("Iterating available platforms... ");
            Console.WriteLine("[{0}]", PlatformIdentifier.AvailablePlatforms.Count);

            BreakLine();

            for (int i = 0; i < PlatformIdentifier.AvailablePlatforms.Count; i++)
            {
                PlatformIdentifier platform = PlatformIdentifier.AvailablePlatforms[i];

                Console.WriteLine("Platform at index [{0}]:", i);
                Console.WriteLine("Profile: {0}", platform.Profile);
                Console.WriteLine("Version: {0}", platform.Version);
                Console.WriteLine("Name: {0}", platform.Name);
                Console.WriteLine("Vendor: {0}", platform.Vendor);
                Console.WriteLine("Extensions [{0}]:", platform.Extensions.Count);
                for (int j = 0; j < platform.Extensions.Count; j++)
                    Console.WriteLine("\tExtension [{0}]: {1}", j, platform.Extensions[j]);

                BreakLine();
            }
        }

        private static void DeviceTest()
        {
            TestHeader("***Device Test***");

            Header("Retrieving available devices for the default platform (0)... ");
            DeviceIdentifier[] devices = null;
            TryExecute(() => devices = PlatformIdentifier.Default.GetDevices(DeviceType.All));

            Console.WriteLine("Available devices: [{0}]", devices.Length);

            BreakLine();

            for (int i = 0; i < devices.Length; i++)
            {
                DeviceIdentifier device = devices[i];
                Console.WriteLine("Device at index [{0}]:", i);
                Console.WriteLine("Type: {0}", device.Type);
                Console.WriteLine("Vendor ID: {0}", device.VendorID);
                Console.WriteLine("Compute Units: {0}", device.ComputeUnits);
                Console.WriteLine("Maximumum Work Item Sizes [{0}]:", device.MaximumWorkItemSizes.Length);
                for (int j = 0; j < device.MaximumWorkItemSizes.Length; j++)
                    Console.WriteLine("\tMaximum Work Item Size [{0}]: {1}", j, device.MaximumWorkItemSizes[j]);
                Console.WriteLine("Maximum Work Group Size: {0}", device.MaximumWorkGroupSize);
                Console.WriteLine("Preffered Byte Vector Width: {0}", device.PreferredVectorWidthByte);
                Console.WriteLine("Preffered Short Vector Width: {0}", device.PreferredVectorWidthShort);
                Console.WriteLine("Preffered Integer Vector Width: {0}", device.PreferredVectorWidthInteger);
                Console.WriteLine("Preffered Long Vector Width: {0}", device.PreferredVectorWidthLong);
                Console.WriteLine("Preffered Single Vector Width: {0}", device.PreferredVectorWidthSingle);
                Console.WriteLine("Preffered Double Vector Width: {0}", device.PreferredVectorWidthDouble);
                Console.WriteLine("Maximum Clock Frequency: {0}", device.MaximumClockFrequency);
                Console.WriteLine("Address Bits: {0}", device.AddressBits);
                Console.WriteLine("Maximum Memory Allocation Size: {0}", device.MaximumMemoryAllocationSize);
                Console.WriteLine("Supports Images: {0}", device.SupportsImages);
                Console.WriteLine("Maximum Image Read Arguments: {0}", device.MaximumImageReadArguments);
                Console.WriteLine("Maximum Image Write Arguments: {0}", device.MaximumImageWriteArguments);
                Console.WriteLine("Maximum 2D Image Width: {0}", device.MaximumImage2DWidth);
                Console.WriteLine("Maximum 2D Image Height: {0}", device.MaximumImage2DHeight);
                Console.WriteLine("Maximum 3D Image Width: {0}", device.MaximumImage3DWidth);
                Console.WriteLine("Maximum 3D Image Height: {0}", device.MaximumImage3DHeight);
                Console.WriteLine("Maximum 3D Image Depth: {0}", device.MaximumImage3DDepth);
                Console.WriteLine("Maximum Samplers: {0}", device.MaximumSamplers);
                Console.WriteLine("Maximum Parameter Size: {0}", device.MaximumParameterSize);
                Console.WriteLine("Base Memory Address Alignment: {0}", device.BaseMemoryAddressAlignment);
                Console.WriteLine("MinimumDataTypeAlignmentSize: {0}", device.MinimumDataTypeAlignmentSize);
                Console.WriteLine("Floating Point Configuration:");
                string[] fpConfig = device.FloatingPointConfiguration.ToString().Split(',');
                foreach (string str in fpConfig)
                    Console.WriteLine("\t" + str.Trim());
                Console.WriteLine("Global Memory Cache Type: {0}", device.GlobalMemoryCacheType);
                Console.WriteLine("Global Memory Cache Line Size: {0}", device.GlobalMemoryCacheLineSize);
                Console.WriteLine("Global Memory Cache Size: {0}", device.GlobalMemoryCacheSize);
                Console.WriteLine("Global Memory Size: {0}", device.GlobalMemorySize);
                Console.WriteLine("Maximum Constant Buffer Size: {0}", device.MaximumConstantBufferSize);
                Console.WriteLine("Maximum Constant Arguments: {0}", device.MaximumConstantArguments);
                Console.WriteLine("Local Memory Type: {0}", device.LocalMemoryType.ToString());
                Console.WriteLine("Local Memory Size: {0}", device.LocalMemorySize);
                Console.WriteLine("Supports Error Correction: {0}", device.SupportsErrorCorrection);
                Console.WriteLine("Profiling Timer Resolution: {0}", device.ProfilingTimerResolution);
                Console.WriteLine("Is Little Endian: {0}", device.EndianLittle);
                Console.WriteLine("Available: {0}", device.Available);
                Console.WriteLine("Compiler Available: {0}", device.CompilerAvailable);
                Console.WriteLine("Execution Capabilities:");
                string[] exCaps = device.ExecutionCapabilities.ToString().Split(',');
                foreach (string str in exCaps)
                    Console.WriteLine("\t" + str.Trim());
                Console.WriteLine("Event Queue Capabilities:");
                string[] cmdCaps = device.CommandQueueCapabilities.ToString().Split(',');
                foreach (string str in cmdCaps)
                    Console.WriteLine("\t" + str.Trim());
                Console.WriteLine("Name: {0}", device.Name);
                Console.WriteLine("Vendor: {0}", device.Vendor);
                Console.WriteLine("Driver Version: {0}", device.DriverVersion);
                Console.WriteLine("Profile: {0}", device.Profile);
                Console.WriteLine("Version: {0}", device.Version);
                Console.WriteLine("Extensions [{0}]:", device.Extensions.Count);
                for (int j = 0; j < device.Extensions.Count; j++)
                    Console.WriteLine("\tExtension [{0}]: {1}", j, device.Extensions[j]);

                BreakLine();

                Console.WriteLine("Platform property equals default platform: {0}", device.Platform == PlatformIdentifier.Default);

                BreakLine();
            }
        }

        private static void ContextTest()
        {
            TestHeader("***Context Test***");

            Header("Creating context using default platform... ");
            TryExecute(() => context = new Context(PlatformIdentifier.Default, DeviceType.All));

            Console.WriteLine("Number of devices: {0}", context.Devices.Count);

            BreakLine();
        }

        private static void CommandQueueTest()
        {
            TestHeader("***Event Queue Test***");

            Header("Creating command queue using first device... ");
            TryExecute(() => commandQueue = new CommandQueue(context, context.Devices[0], new CommandQueueDescription(false, false)));

            Header("Setting command queue properties... ");
            TryExecute(() =>
            {
                commandQueue.OutOfOrderExecutionEnabled = commandQueue.Device.CommandQueueCapabilities.SupportsOutOfOrderExecution;
                commandQueue.ProfilingEnabled = commandQueue.Device.CommandQueueCapabilities.SupportsProfiling;
            });

            Console.WriteLine("Out of order execution enabled: {0}", commandQueue.OutOfOrderExecutionEnabled);
            Console.WriteLine("Profiling enabled: {0}", commandQueue.ProfilingEnabled);

            BreakLine();
        }

        private static void BufferTest()
        {
            TestHeader("***Buffer Test***");

            Header("Creating memory object... ");
            TryExecute(() => memoryObject = MemoryObject.CreateBuffer(context, MemoryAccessFlags.ReadWrite, typeof(float), dataLength));
            Console.WriteLine("Type: {0}", memoryObject.Type);
            Console.WriteLine("Size: {0}", memoryObject.Size);
            BreakLine();

            Random random = new Random();
            Header("Setting data... ");
            BreakLine();
            for (int i = 0; i < data.Length; i++)
            {
                data[i] = (float)random.NextDouble();
                Console.WriteLine("Data [{0}]: {1}", i, data[i]);
            }
            BreakLine();

            Header("Writing memory object... ");
            List<Event> commandList = new List<Event>();
            TryExecute(() => commandList.Add(commandQueue.EnqueueWriteBuffer<float>(memoryObject, true, 0, data, null)));
            Console.WriteLine("Event type: {0}", commandList.Last().Type);
            Console.WriteLine("Event status: {0}", commandList.Last().Status);
            BreakLine();

            Header("Reading memory object... ");
            TryExecute(() => commandList.Add(commandQueue.EnqueueReadBuffer<float>(memoryObject, true, 0, newData, commandList.ToArray())));
            Console.WriteLine("Event type: {0}", commandList.Last().Type);
            Console.WriteLine("Event status: {0}", commandList.Last().Status);
            BreakLine();

            Header("Flushing and finishing command queue... ");
            TryExecute(() =>
                {
                    commandQueue.Flush();
                    commandQueue.Finish();
                });
            BreakLine();

            Header("Displaying readback... ");
            BreakLine();

            for (int i = 0; i < newData.Length; i++)
            {
                Console.WriteLine("Data [{0}]: {1}", i, newData[i]);
            }
            BreakLine();

            Header("Creating second buffer... ");
            MemoryObject secondBuffer = null;
            TryExecute(() => secondBuffer = MemoryObject.CreateBuffer(context, MemoryAccessFlags.ReadWrite, typeof(float), dataLength));
            Console.WriteLine("Type: {0}", secondBuffer.Type);
            Console.WriteLine("Size: {0}", secondBuffer.Size);
            BreakLine();

            Header("Copying buffers... ");
            TryExecute(() => commandList.Add(commandQueue.EnqueueCopyBuffer<float>(memoryObject, secondBuffer, commandList.ToArray())));
            BreakLine();

            Header("Reading memory object... ");
            TryExecute(() => commandList.Add(commandQueue.EnqueueReadBuffer<float>(secondBuffer, true, 0, newData, commandList.ToArray())));
            Console.WriteLine("Event type: {0}", commandList.Last().Type);
            Console.WriteLine("Event status: {0}", commandList.Last().Status);
            BreakLine();

            Header("Flushing and finishing command queue... ");
            TryExecute(() =>
            {
                commandQueue.Flush();
                commandQueue.Finish();
            });
            BreakLine();

            Header("Displaying readback... ");
            BreakLine();
            for (int i = 0; i < newData.Length; i++)
            {
                Console.WriteLine("Data [{0}]: {1}", i, newData[i]);
            }
            BreakLine();

            Header("Disposing of commands... ");
            TryExecute(() =>
            {
                foreach (Event command in commandList)
                    command.Dispose();
            });
            BreakLine();

            Header("Disposing of second memory object... ");
            TryExecute(() =>
            {
                secondBuffer.Dispose();
            });
            BreakLine();

            ObjectTracker.ContainsObject(IntPtr.Zero);
        }

        private static void Image2DTest()
        {
            TestHeader("***Image 2D Test***");

            List<Event> commandList = new List<Event>();

            Header("Creating 2D image object... ");
            TryExecute(() => image2DObject = MemoryObject.CreateImage2D(context, MemoryAccessFlags.ReadWrite, ImageFormat.R32, 2, 2));
            Console.WriteLine("Type: {0}", image2DObject.Type);
            Console.WriteLine("Size: {0}", image2DObject.Size);
            BreakLine();

            Header("Setting data... ");
            TryExecute(() => commandList.Add(commandQueue.EnqueueWriteImage<float>(image2DObject, true, data, commandList.ToArray())));
            Console.WriteLine("Event type: {0}", commandList.Last().Type);
            Console.WriteLine("Event status: {0}", commandList.Last().Status);
            BreakLine();

            Header("Reading data... ");
            TryExecute(() => commandList.Add(commandQueue.EnqueueReadImage<float>(image2DObject, true, newData, commandList.ToArray())));
            Console.WriteLine("Event type: {0}", commandList.Last().Type);
            Console.WriteLine("Event status: {0}", commandList.Last().Status);
            BreakLine();

            Header("Flushing and finishing command queue... ");
            TryExecute(() =>
            {
                commandQueue.Flush();
                commandQueue.Finish();
            });
            BreakLine();

            Header("Displaying readback... ");
            BreakLine();
            for (int i = 0; i < newData.Length; i++)
            {
                Console.WriteLine("Data [{0}]: {1}", i, newData[i]);
            }
            BreakLine();

            Header("Creating second image... ");
            MemoryObject secondImage = null;
            TryExecute(() => secondImage = MemoryObject.CreateImage2D(context, MemoryAccessFlags.ReadWrite, ImageFormat.R32, 2, 2));
            Console.WriteLine("Type: {0}", secondImage.Type);
            Console.WriteLine("Size: {0}", secondImage.Size);
            BreakLine();

            Header("Copying buffers... ");
            TryExecute(() => commandList.Add(commandQueue.EnqueueCopyImage(image2DObject, secondImage, commandList.ToArray())));
            BreakLine();

            Header("Reading second image... ");
            TryExecute(() => commandList.Add(commandQueue.EnqueueReadImage<float>(secondImage, true, newData, commandList.ToArray())));
            Console.WriteLine("Event type: {0}", commandList.Last().Type);
            Console.WriteLine("Event status: {0}", commandList.Last().Status);
            BreakLine();

            Header("Flushing and finishing command queue... ");
            TryExecute(() =>
            {
                commandQueue.Flush();
                commandQueue.Finish();
            });
            BreakLine();

            Header("Displaying readback... ");
            BreakLine();
            for (int i = 0; i < newData.Length; i++)
            {
                Console.WriteLine("Data [{0}]: {1}", i, newData[i]);
            }
            BreakLine();

            Header("Disposing of commands... ");
            TryExecute(() =>
            {
                foreach (Event command in commandList)
                    command.Dispose();
            });
            BreakLine();

            Header("Disposing of second memory object... ");
            TryExecute(() =>
            {
                secondImage.Dispose();
            });
            BreakLine();
        }

        private static void Image3DTest()
        {
            TestHeader("***Image 3D Test***");

            List<Event> commandList = new List<Event>();

            Header("Creating 3D image object... ");
            TryExecute(() => image3DObject = MemoryObject.CreateImage3D(context, MemoryAccessFlags.ReadWrite, ImageFormat.R32, 2, 2, 1));
            Console.WriteLine("Type: {0}", image3DObject.Type);
            Console.WriteLine("Size: {0}", image3DObject.Size);
            BreakLine();

            Header("Setting data... ");
            TryExecute(() => commandList.Add(commandQueue.EnqueueWriteImage<float>(image3DObject, true, data, commandList.ToArray())));
            Console.WriteLine("Event type: {0}", commandList.Last().Type);
            Console.WriteLine("Event status: {0}", commandList.Last().Status);
            BreakLine();

            Header("Reading data... ");
            TryExecute(() => commandList.Add(commandQueue.EnqueueReadImage<float>(image3DObject, true, newData, commandList.ToArray())));
            Console.WriteLine("Event type: {0}", commandList.Last().Type);
            Console.WriteLine("Event status: {0}", commandList.Last().Status);
            BreakLine();

            Header("Flushing and finishing command queue... ");
            TryExecute(() =>
            {
                commandQueue.Flush();
                commandQueue.Finish();
            });
            BreakLine();

            Header("Displaying readback... ");
            BreakLine();
            for (int i = 0; i < newData.Length; i++)
            {
                Console.WriteLine("Data [{0}]: {1}", i, newData[i]);
            }
            BreakLine();

            Header("Creating second image... ");
            MemoryObject secondImage = null;
            TryExecute(() => secondImage = MemoryObject.CreateImage3D(context, MemoryAccessFlags.ReadWrite, ImageFormat.R32, 2, 2, 1));
            Console.WriteLine("Type: {0}", secondImage.Type);
            Console.WriteLine("Size: {0}", secondImage.Size);
            BreakLine();

            Header("Copying buffers... ");
            TryExecute(() => commandList.Add(commandQueue.EnqueueCopyImage(image3DObject, secondImage, commandList.ToArray())));
            BreakLine();

            Header("Reading second image... ");
            TryExecute(() => commandList.Add(commandQueue.EnqueueReadImage<float>(secondImage, true, newData, commandList.ToArray())));
            Console.WriteLine("Event type: {0}", commandList.Last().Type);
            Console.WriteLine("Event status: {0}", commandList.Last().Status);
            BreakLine();

            Header("Flushing and finishing command queue... ");
            TryExecute(() =>
            {
                commandQueue.Flush();
                commandQueue.Finish();
            });
            BreakLine();

            Header("Displaying readback... ");
            BreakLine();
            for (int i = 0; i < newData.Length; i++)
            {
                Console.WriteLine("Data [{0}]: {1}", i, newData[i]);
            }
            BreakLine();

            Header("Disposing of commands... ");
            TryExecute(() =>
            {
                foreach (Event command in commandList)
                    command.Dispose();
            });
            BreakLine();

            Header("Disposing of second memory object... ");
            TryExecute(() =>
            {
                secondImage.Dispose();
            });
            BreakLine();
        }

        private static void Image2D3DCopyTest()
        {
            TestHeader("***Image 2D to 3D Copy Test***");
            List<Event> commandList = new List<Event>();

            Header("Resetting 3D image data... ");
            TryExecute(() => commandList.Add(commandQueue.EnqueueWriteImage<float>(image3DObject, true, new float[] { 0, 0, 0, 0 }, commandList.ToArray())));
            BreakLine();

            Header("Reading 3D image... ");
            TryExecute(() => commandList.Add(commandQueue.EnqueueReadImage<float>(image3DObject, true, newData, commandList.ToArray())));
            Console.WriteLine("Event type: {0}", commandList.Last().Type);
            Console.WriteLine("Event status: {0}", commandList.Last().Status);
            BreakLine();

            Header("Flushing and finishing command queue... ");
            TryExecute(() =>
            {
                commandQueue.Flush();
                commandQueue.Finish();
            });
            BreakLine();

            Header("Displaying readback... ");
            BreakLine();
            for (int i = 0; i < newData.Length; i++)
            {
                Console.WriteLine("Data [{0}]: {1}", i, newData[i]);
            }
            BreakLine();

            Header("Copying 2D image to 3D image... ");
            TryExecute(() => commandList.Add(commandQueue.EnqueueCopyImage(image2DObject, image3DObject, commandList.ToArray())));
            BreakLine();

            Header("Reading 3D image... ");
            TryExecute(() => commandList.Add(commandQueue.EnqueueReadImage<float>(image3DObject, true, newData, commandList.ToArray())));
            Console.WriteLine("Event type: {0}", commandList.Last().Type);
            Console.WriteLine("Event status: {0}", commandList.Last().Status);
            BreakLine();

            Header("Flushing and finishing command queue... ");
            TryExecute(() =>
            {
                commandQueue.Flush();
                commandQueue.Finish();
            });
            BreakLine();

            Header("Displaying readback... ");
            BreakLine();
            for (int i = 0; i < newData.Length; i++)
            {
                Console.WriteLine("Data [{0}]: {1}", i, newData[i]);
            }
            BreakLine();

            Header("Disposing of commands... ");
            TryExecute(() =>
            {
                foreach (Event command in commandList)
                    command.Dispose();
            });
            BreakLine();
        }

        private static void Image3D2DCopyTest()
        {
            TestHeader("***Image 3D to 2D Copy Test***");
            List<Event> commandList = new List<Event>();

            Header("Resetting 2D image data... ");
            TryExecute(() => commandList.Add(commandQueue.EnqueueWriteImage<float>(image2DObject, true, new float[] { 0, 0, 0, 0 }, commandList.ToArray())));
            BreakLine();

            Header("Reading 2D image... ");
            TryExecute(() => commandList.Add(commandQueue.EnqueueReadImage<float>(image2DObject, true, newData, commandList.ToArray())));
            Console.WriteLine("Event type: {0}", commandList.Last().Type);
            Console.WriteLine("Event status: {0}", commandList.Last().Status);
            BreakLine();

            Header("Flushing and finishing command queue... ");
            TryExecute(() =>
            {
                commandQueue.Flush();
                commandQueue.Finish();
            });
            BreakLine();

            Header("Displaying readback... ");
            BreakLine();
            for (int i = 0; i < newData.Length; i++)
            {
                Console.WriteLine("Data [{0}]: {1}", i, newData[i]);
            }
            BreakLine();

            Header("Copying 3D image to 2D image... ");
            TryExecute(() => commandList.Add(commandQueue.EnqueueCopyImage(image3DObject, image2DObject, commandList.ToArray())));
            BreakLine();

            Header("Reading 2D image... ");
            TryExecute(() => commandList.Add(commandQueue.EnqueueReadImage<float>(image2DObject, true, newData, commandList.ToArray())));
            Console.WriteLine("Event type: {0}", commandList.Last().Type);
            Console.WriteLine("Event status: {0}", commandList.Last().Status);
            BreakLine();

            Header("Flushing and finishing command queue... ");
            TryExecute(() =>
            {
                commandQueue.Flush();
                commandQueue.Finish();
            });
            BreakLine();

            Header("Displaying readback... ");
            BreakLine();
            for (int i = 0; i < newData.Length; i++)
            {
                Console.WriteLine("Data [{0}]: {1}", i, newData[i]);
            }
            BreakLine();

            Header("Disposing of commands... ");
            TryExecute(() =>
            {
                foreach (Event command in commandList)
                    command.Dispose();
            });
            BreakLine();
        }

        private static void SamplerTest()
        {
            TestHeader("***Sampler Test***");

            Header("Creating sampler... ");
            TryExecute(() => sampler = new Sampler(context, true, AddressingMode.Repeat, FilterMode.Linear));
            Console.WriteLine("Normalized Coordinates: {0}", sampler.NormalizedCoordinates);
            Console.WriteLine("Addressing Mode: {0}", sampler.AddressingMode);
            Console.WriteLine("Filter Mode: {0}", sampler.FilterMode);
            BreakLine();
        }

        private static void ProgramTest()
        {
            TestHeader("***Program Test***");

            Header("Creating program... ");
            TryExecute(() => program = Program.FromFile(context, "TestProgram.cl"));
            BreakLine();

            Header("Retrieving build status... ");
            BuildStatus buildStatus = BuildStatus.None;
            TryExecute(() => buildStatus = program.GetBuildStatus(program.Devices[0]));
            Console.WriteLine("Build Status: {0}", buildStatus);
            BreakLine();

            string[] buildLogs = null;
            Header("Building program... ");
            TryExecute(() => program.Build(context.Devices, null, out buildLogs));
            for (int i = 0; i < buildLogs.Length; i++)
                Console.WriteLine("Build Log {0}: {1}", i, buildLogs[i]);
            BreakLine();

            Header("Retrieving build status... ");
            TryExecute(() => buildStatus = program.GetBuildStatus(program.Devices[0]));
            Console.WriteLine("Build Status: {0}", buildStatus);
            BreakLine();

            Header("Retrieving source code...");
            string source = null;
            TryExecute(() => source = program.GetSource());
            Console.WriteLine(source);
            BreakLine();

            Header("Retrieving binaries... ");
            ProgramBinary[] binaries = null;
            TryExecute(() => binaries = program.GetBinaries());
            BreakLine();

            Header("Saving binaries... ");
            TryExecute(() =>
                {
                    for (int i = 0; i < binaries.Length; i++)
                    {
                        File.WriteAllBytes("Binary" + i.ToString() + ".txt", binaries[i].BinaryData);
                    }
                });
            BreakLine();

            Header("Reloading binaries from file... ");
            binaries = new ProgramBinary[binaries.Length];
            TryExecute(() =>
                {
                    for (int i = 0; i < binaries.Length; i++)
                        binaries[i] = ProgramBinary.FromFile("Binary" + i.ToString() + ".txt", program.Devices[i]);
                });
            BreakLine();

            Header("Iterating binaries... ");
            BreakLine();
            for (int i = 0; i < binaries.Length; i++)
            {
                Console.WriteLine("Binaries [{0}]:", i);
                Console.WriteLine(ASCIIEncoding.ASCII.GetString(binaries[i].BinaryData));
                BreakLine();
            }

            Header("Creating program from binaries... ");
            Program binaryProgram = null;
            TryExecute(() => binaryProgram = Program.FromBinaries(context, binaries));
            BreakLine();

            Header("Building program... ");
            TryExecute(() => binaryProgram.Build(context.Devices.ToArray(), null));
            BreakLine();

            Header("Disposing of program from binaries... ");
            TryExecute(() => binaryProgram.Dispose());
            BreakLine();
        }

        private static void KernelTest()
        {
            TestHeader("***Kernel Test***");

            Header("Creating kernel... ");
            TryExecute(() => kernel = program.Kernels["VectorAdd"]);
            Console.WriteLine("Function Name: {0}", kernel.FunctionName);
            Console.WriteLine("Argument Count: {0}", kernel.ArgumentCount);
            BreakLine();

            Header("Setting arguments... ");
            TryExecute(() =>
                {
                    kernel.SetArgument(0, memoryObject);
                    kernel.SetArgument(1, memoryObject);
                    kernel.SetArgument(2, memoryObject);
                    kernel.SetArgument(3, dataLength);
                });
            BreakLine();
            Header("Reading memory object... ");

            List<Event> commandList = new List<Event>();

            TryExecute(() => commandList.Add(commandQueue.EnqueueReadBuffer<float>(memoryObject, true, 0, newData, commandList.ToArray())));
            Console.WriteLine("Event type: {0}", commandList.Last().Type);
            Console.WriteLine("Event status: {0}", commandList.Last().Status);
            BreakLine();

            Header("Flushing and finishing command queue... ");
            TryExecute(() =>
            {
                commandQueue.Flush();
                commandQueue.Finish();
            });
            BreakLine();

            Header("Displaying readback... ");
            BreakLine();
            for (int i = 0; i < newData.Length; i++)
            {
                Console.WriteLine("Data [{0}]: {1}", i, newData[i]);
            }
            BreakLine();

            Header("Executing kernel... ");
            TryExecute(() => commandList.Add(commandQueue.EnqueueNDRangeKernel(kernel, new int[] { 4 }, new int[] { 1 }, commandList.ToArray())));
            BreakLine();
            
            TryExecute(() => commandList.Add(commandQueue.EnqueueReadBuffer<float>(memoryObject, true, 0, newData, commandList.ToArray())));
            Console.WriteLine("Event type: {0}", commandList.Last().Type);
            Console.WriteLine("Event status: {0}", commandList.Last().Status);
            BreakLine();

            Header("Flushing and finishing command queue... ");
            TryExecute(() =>
            {
                commandQueue.Flush();
                commandQueue.Finish();
            });
            BreakLine();

            Header("Displaying readback... ");
            BreakLine();
            for (int i = 0; i < newData.Length; i++)
            {
                Console.WriteLine("Data [{0}]: {1}", i, newData[i]);
            }
            BreakLine();

            Header("Disposing of commands... ");
            TryExecute(() =>
            {
                foreach (Event command in commandList)
                    command.Dispose();
            });
            BreakLine();
        }

        private static void DisposalTest()
        {
            TestHeader("***Disposal Test***");

            Header("Disposing of objects... ");
            TryExecute(() =>
                {
                    context.Dispose();
                    commandQueue.Device.Dispose();
                    commandQueue.Dispose();
                    memoryObject.Dispose();
                    image2DObject.Dispose();
                    image3DObject.Dispose();
                    sampler.Dispose();
                    program.Dispose();
                });
            BreakLine();

            Header("Remaining Objects... ");
            Console.WriteLine("[{0}]", ObjectTracker.Objects.Count());
            foreach (LuminalObject obj in ObjectTracker.Objects)
            {
                Console.WriteLine(obj);
            }
            Console.WriteLine("* Note: All Platform Identifiers are automatically disposed on program exit.");
            BreakLine();
        }
    }
}
